/*
 * BlackLightning.SystemModule
 * Andy Tidball
 * 
 * Project: Black Lightning
 * Copyright: GNU General Public License
 */

using System;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using BlackLightning.PluginLib;
using BlackLightning.PluginLib.Modules;
using BlackLightning.PluginLib.Modules.Packets;
using BlackLightning.Properties;

namespace BlackLightning {
	/// <summary>
	/// Handles all of Black Lightning's built-in commands and functionality.
	/// </summary>
	internal class SystemModule : global::BlackLightning.PluginLib.Modules.Module {
		// member data
		private BlackLightning _BlackLightning; // the Black Lightning instance that we're handling commands for

		/// <summary>
		/// Creates a new SystemModule.
		/// </summary>
		/// <param name="BL">The BlackLightning instance that we'll be handling commands for.</param>
		public SystemModule(BlackLightning BL) {
			_BlackLightning = BL;
		}

		/// <summary>
		/// Retrieves the value of one of the module's properties.
		/// </summary>
		/// <param name="Property">The property to retrieve the value for.</param>
		/// <returns>The value of the requested property, or null if this module doesn't handle it.</returns>
		public override object GetProperty(ModuleProperty Property) {
			switch (Property) {
				case ModuleProperty.HelpText: return Resources.Help;
				default:                      return null;
			}
		}

		/// <summary>
		/// Called whenever Black Lightning has a message for us.
		/// </summary>
		/// <param name="Message">The message we need to handle.</param>
		public override void HandleMessage(global::BlackLightning.PluginLib.Message Message) {
			if (Message is LoadedMessage) {
				HandleMessage_Loaded(Message as LoadedMessage);
			}
		}

		/// <summary>
		/// Called when we are loaded.
		/// </summary>
		/// <param name="Message">The loaded message that we received.</param>
		public void HandleMessage_Loaded(LoadedMessage Message) {
			// register for the @load and @unload commands
			base.RegisterPacket(new CommandPacketFilter(Program.FrontEnd, Program.BlackLightning, "load"));
			base.RegisterPacket(new CommandPacketFilter(Program.FrontEnd, Program.BlackLightning, "unload"));
			
			// register for the @help and @about commands
			base.RegisterPacket(new CommandPacketFilter(Program.FrontEnd, Program.BlackLightning, "help"));
			base.RegisterPacket(new CommandPacketFilter(Program.FrontEnd, Program.BlackLightning, "about"));
			
			// register for the @exit and @quit commands
			base.RegisterPacket(new CommandPacketFilter(Program.FrontEnd, Program.BlackLightning, "exit"));
			base.RegisterPacket(new CommandPacketFilter(Program.FrontEnd, Program.BlackLightning, "quit"));

			// register for the @reconnect command
			base.RegisterPacket(new CommandPacketFilter(Program.FrontEnd, Program.BlackLightning, "reconnect"));

			// register for the @options command
			base.RegisterPacket(new CommandPacketFilter(Program.FrontEnd, Program.BlackLightning, "options"), Priority.Normal, true);
		}

		/// <summary>
		/// Called whenever a Packet that we registered for has been received.
		/// </summary>
		/// <param name="Packet">The packet we need to handle.</param>
		public override void HandlePacket(ref BLPacket Packet) {
			CommandPacket P = Packet as CommandPacket;
			switch (P.Command) {
				case "help":      HandleCommand_Help(P);      break;
				case "about":     HandleCommand_About(P);     break;
				case "load":      HandleCommand_Load(P);      break;
				case "unload":    HandleCommand_Unload(P);    break;
				case "reconnect": HandleCommand_Reconnect(P); break;
				case "options":   HandleCommand_Options(P);   break;
				case "exit":      HandleCommand_Exit(P);      break;
				case "quit":      HandleCommand_Exit(P);      break;
			}
		}

		/// <summary>
		/// Handles the @help command.
		/// </summary>
		/// <param name="P">The command packet received.</param>
		public void HandleCommand_Help(CommandPacket P) {
			// check if there are any arguments
			if (P.Arguments.Length == 0) {
				// send the Black Lightning help text
				base.SendToFrontEnd(Resources.Help);
				P.Suppress();
			} else if (P.Arguments.Length == 1) {
				// if we only have one argument, check for a module with that full name
				Module M = null;
				try {
					M = _BlackLightning.PluginManager.GetModule(P.Arguments[0]);
				} catch (ArgumentException) {
					base.SendToFrontEnd("Several modules named '" + P.Arguments[0] + "' are currently loaded from different plugins.  Please be more specific by specifying a plugin.");
					P.Suppress();
				}
				if (M == null) {
					// no module with that full name, check for a plugin
					Plugin Plugin = _BlackLightning.PluginManager.GetPlugin(P.Arguments[0]);
					if (Plugin == null) {
						// no plugin either, send an error
						base.SendToFrontEnd("No plugin or module with the name '" + P.Arguments[0] + "' is currently loaded.");
						P.Suppress();
					} else {
						// we found a plugin, send its help text
						base.SendToFrontEnd(Plugin.HelpText);
						P.Suppress();
					}
				} else {
					// we found a module, send its help text
					base.SendToFrontEnd(M.HelpText);
					P.Suppress();
				}
			} else if (P.Arguments.Length >= 2) {
				// check for a module with the given plugin and module name
				Module M = _BlackLightning.PluginManager.GetModule(P.Arguments[0], P.Arguments[1]);
				if (M == null) {
					// no module found, send an error
					base.SendToFrontEnd("No module with the name '" + P.Arguments[1] + "' is currently loaded from a plugin named '" + P.Arguments[0] + "'.");
					P.Suppress();
				} else {
					// we found a module, send its help text
					base.SendToFrontEnd(M.HelpText);
					P.Suppress();
				}
			}
		}

		/// <summary>
		/// Handles the @about command.
		/// </summary>
		/// <param name="P">The command packet received.</param>
		public void HandleCommand_About(CommandPacket P) {
			// check if we have an argument
			if (P.Arguments.Length == 0) {
				// send the about text
				base.SendToFrontEnd(string.Format(Resources.About, Application.ProductVersion));
			} else {
				// load info on the given plugin
				try {
					PluginInfo Info = new PluginInfo(P.Arguments[0]);
					base.SendToFrontEnd(Info.FormatForDisplay());
				} catch (LoadException x) {
					base.Send(new ExceptionPacket(x, true));
				}
			}
			P.Suppress();
		}

		/// <summary>
		/// Handles the @load command.
		/// </summary>
		/// <param name="P">The command packet received.</param>
		public void HandleCommand_Load(CommandPacket P) {
			// make sure we have arguments
			if (P.Arguments.Length == 0) {
				base.SendToFrontEnd(Resources.LoadUsage);
			}

			// have the plugin manager handle the loading
			if (P.Arguments.Length == 1) {
				_BlackLightning.PluginManager.Load(P.Arguments[0]);
			} else if (P.Arguments.Length >= 2) {
				_BlackLightning.PluginManager.Load(P.Arguments[0], P.Arguments[1]);
			}
		}

		/// <summary>
		/// Handles the @unload command.
		/// </summary>
		/// <param name="P">The command packet received.</param>
		public void HandleCommand_Unload(CommandPacket P) {
			// make sure we have arguments
			if (P.Arguments.Length == 0) {
				base.SendToFrontEnd(Resources.UnloadUsage);
			}

			// have the plugin manager handle the unloading
			if (P.Arguments.Length == 1) {
				_BlackLightning.PluginManager.Unload(P.Arguments[0]);
			} else if (P.Arguments.Length >= 2) {
				_BlackLightning.PluginManager.Unload(P.Arguments[0], P.Arguments[1]);
			}
		}

		/// <summary>
		/// Handles the @exit command.
		/// </summary>
		/// <param name="P">The command packet received.</param>
		public void HandleCommand_Exit(CommandPacket P) {
			// have the front end exit
			// when it disconnects the dispatcher will stop and kill the program
			_BlackLightning.Dispatcher.FrontEnd.Exit();
		}

		/// <summary>
		/// Handles the @reconnect command.
		/// </summary>
		/// <param name="P">The command packet received.</param>
		public void HandleCommand_Reconnect(CommandPacket P) {
			// run the reconnect routine
			_BlackLightning.Reconnect();
		}

		/// <summary>
		/// Handles the @options command.
		/// </summary>
		/// <param name="P">The command packet received.</param>
		public void HandleCommand_Options(CommandPacket P) {
			// show the options dialog
			_BlackLightning.ShowPluginOptions();
		}
	}
}
